/*
 * H265MediaSubsession.cpp
 *
 *  Created on: 2016年4月7日
 *      Author: terry
 */

#include "H265MediaSubsession.h"
#include "H264FramedSource.h"
#include "H265VideoStreamDiscreteFramer.hh"
#include "H265VideoRTPSink.hh"

#include "TStringUtil.h"
#include "Base64.h"
#include "CasterChannelTable.h"
#include "H265PropParser.h"
#include "Base64.hh"

#include "StopWatch.h"
#include "CLog.h"
#include "GroupsockHelper.hh"

namespace av
{

H265MediaSubsession::H265MediaSubsession(UsageEnvironment& env, portNumBits initPort, MediaSourcePtr mediaSource, const std::string& entry):
		OnDemandServerMediaSubsession(env, false, initPort),
		m_mediaSource(mediaSource),
		m_entry(entry)
{
}

H265MediaSubsession::~H265MediaSubsession()
{
}


#pragma warning(disable:4996)

char const* H265MediaSubsession::sdpLines()
{
    if (fSDPLines != NULL)
    {
        return fSDPLines;
    }

    //return OnDemandServerMediaSubsession::sdpLines();
    MediaFormat fmt;
    m_mediaSource->getMediaFormat(fmt);

    unsigned estBitrate = fmt.m_bitrate > 0 ? fmt.m_bitrate : 500;
    char const* mediaType = "video";
    unsigned char rtpPayloadType = 96;
    AddressString ipAddressStr(fServerAddressForSDP);
    const char* rtpmapLine = "a=rtpmap:96 H265/90000\r\n";
    char const* rtcpmuxLine = "";
    char const* rangeLine = rangeSDPLine();

    std::string fmtp = getFmtp(rtpPayloadType, fmt);

	char const* const sdpFmt =
		"m=%s %u RTP/AVP %d\r\n"
		"c=IN IP4 %s\r\n"
		"b=AS:%u\r\n"
        //"a=recvonly\r\n"
		"%s"
		"%s"
		"%s"
		"%s"
		"a=control:%s\r\n";
	unsigned sdpFmtSize = strlen(sdpFmt)
		+ strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */
		+ strlen(ipAddressStr.val())
		+ 20 /* max int len */
		+ strlen(rtpmapLine)
		+ strlen(rtcpmuxLine)
		+ strlen(rangeLine)
		+ fmtp.size()
		+ strlen(trackId());
	char* sdpLines = new char[sdpFmtSize];
	sprintf(sdpLines, sdpFmt,
		mediaType, // m= <media>
		fPortNumForSDP, // m= <port>
		rtpPayloadType, // m= <fmt list>
		ipAddressStr.val(), // c= address
		estBitrate, // b=AS:<bandwidth>
		rtpmapLine, // a=rtpmap:... (if present)
		rtcpmuxLine, // a=rtcp-mux:... (if present)
		rangeLine, // a=range:... (if present)
		fmtp.c_str(), // optional extra SDP line
		trackId()); // a=control:<track-id>
	delete[] (char*)rangeLine;

	fSDPLines = strDup(sdpLines);
	delete[] sdpLines;

    return fSDPLines;
}

FramedSource* H265MediaSubsession::createNewStreamSource(unsigned clientSessionId,
			          unsigned& estBitrate)
{
    comn::StopWatch watch;

	MediaSourcePtr mediaSource = CasterChannelTable::instance().findWithName(m_entry);
	if (!mediaSource)
	{
		mediaSource = m_mediaSource;
	}

	MediaFormat fmt;
	mediaSource->getMediaFormat(fmt);

	estBitrate = fmt.m_bitrate / 1000;

	H264FramedSource* pSource = new H264FramedSource(envir(), mediaSource, fmt, fmt.m_clockRate);

	FramedSource* src = H265VideoStreamDiscreteFramer::createNew(envir(), pSource);

    if (watch.elapse() > 50)
    {
        CLog::warning("H265MediaSubsession::createNewStreamSource consume too much time. %d\n", watch.elapse());
    }

    return src;
}

  // "estBitrate" is the stream's estimated bitrate, in kbps
RTPSink* H265MediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock,
		        unsigned char rtpPayloadTypeIfDynamic,
		        FramedSource* inputSource)
{
	setSendBufferTo(envir(), rtpGroupsock->socketNum(), 1024 * 1024 * 1);
	//unsigned int sendSize = getSendBufferSize(envir(), rtpGroupsock->socketNum());

	OutPacketBuffer::increaseMaxSizeTo(1024 * 1024);

    H265VideoRTPSink* sink = H265VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);

    //sink->setPacketSizes(1000, 1300);
	//sink->enableRTCPReports() = false;

	return sink;
}

void H265MediaSubsession::setStreamSourceScale(FramedSource* inputSource, float scale)
{

}

void H265MediaSubsession::closeStreamSource(FramedSource* inputSource)
{

    OnDemandServerMediaSubsession::closeStreamSource(inputSource);
}

std::string H265MediaSubsession::getFmtp(int pt, const MediaFormat& fmt)
{
    if (!fmt.m_videoProp.empty())
    {
        std::string vps;
        std::string sps;
        std::string pps;
        if (H265PropParser::splitPropSet((uint8_t*)fmt.m_videoProp.c_str(), fmt.m_videoProp.size(),
            vps, sps, pps))
        {
            return makeFmtp(pt, vps, sps, pps);
        }
    }

    std::string line;
    line = comn::StringUtil::format("a=fmtp:%d", pt);
    line += "\r\n";
    return line;
}


static unsigned removeH264or5EmulationBytes(u_int8_t* to, unsigned toMaxSize, const char* from, unsigned fromSize) 
{
    unsigned toSize = 0;
    unsigned i = 0;
    while (i < fromSize && toSize+1 < toMaxSize) {
     if (i+2 < fromSize && from[i] == 0 && from[i+1] == 0 && from[i+2] == 3) {
         to[toSize] = to[toSize+1] = 0;
         toSize += 2;
         i += 3;
     } else {
         to[toSize] = from[i];
         toSize += 1;
         i += 1;
     }
    }
    return toSize;
}

std::string H265MediaSubsession::makeFmtp(int pt, const std::string& vps, const std::string& sps, const std::string& pps)
{
    u_int8_t* vpsWEB = new u_int8_t[vps.size()]; // "WEB" means "Without Emulation Bytes"
    unsigned vpsWEBSize = removeH264or5EmulationBytes(vpsWEB, vps.size(), vps.c_str(), vps.size());
    if (vpsWEBSize < 6/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) {
        // Bad VPS size => assume our source isn't ready
        delete[] vpsWEB;
        return std::string();
    }

    u_int8_t const* profileTierLevelHeaderBytes = &vpsWEB[6];
    unsigned profileSpace  = profileTierLevelHeaderBytes[0]>>6; // general_profile_space
    unsigned profileId = profileTierLevelHeaderBytes[0]&0x1F; // general_profile_idc
    unsigned tierFlag = (profileTierLevelHeaderBytes[0]>>5)&0x1; // general_tier_flag
    unsigned levelId = profileTierLevelHeaderBytes[11]; // general_level_idc
    u_int8_t const* interop_constraints = &profileTierLevelHeaderBytes[5];
    char interopConstraintsStr[100];
    sprintf(interopConstraintsStr, "%02X%02X%02X%02X%02X%02X", 
        interop_constraints[0], interop_constraints[1], interop_constraints[2],
        interop_constraints[3], interop_constraints[4], interop_constraints[5]);
    delete[] vpsWEB;

    char* sprop_vps = base64Encode((char*)vps.c_str(), vps.size());
    char* sprop_sps = base64Encode((char*)sps.c_str(), sps.size());
    char* sprop_pps = base64Encode((char*)pps.c_str(), pps.size());

    char const* fmtpFmt =
        "a=fmtp:%d profile-space=%u"
        ";profile-id=%u"
        ";tier-flag=%u"
        ";level-id=%u"
        ";interop-constraints=%s"
        ";sprop-vps=%s"
        ";sprop-sps=%s"
        ";sprop-pps=%s\r\n";
    unsigned fmtpFmtSize = strlen(fmtpFmt)
        + 3 /* max num chars: rtpPayloadType */ + 20 /* max num chars: profile_space */
        + 20 /* max num chars: profile_id */
        + 20 /* max num chars: tier_flag */
        + 20 /* max num chars: level_id */
        + strlen(interopConstraintsStr)
        + strlen(sprop_vps)
        + strlen(sprop_sps)
        + strlen(sprop_pps);

    char fmtp[1024]; 
    sprintf(fmtp, fmtpFmt,
        pt, profileSpace,
        profileId,
        tierFlag,
        levelId,
        interopConstraintsStr,
        sprop_vps,
        sprop_sps,
        sprop_pps);

    delete[] sprop_vps;
    delete[] sprop_sps;
    delete[] sprop_pps;

    return fmtp;
}



} /* namespace av */
